import interact from 'interactjs';
import { IPanelItem } from '@ibiz/model-core';
import { Interactable } from '@interactjs/types/index';
import { PanelContainerController } from '@ibiz-template/runtime';
import { PanelDraggableContainerState } from './panel-draggable-container.state';

interface IDargOpt {
  /**
   * @description 允许拖拽元素
   * @type {boolean}
   * @memberof IDargOpt
   */
  allowDrag: boolean;
  /**
   * @description 允许拖拽边
   * @type {boolean}
   * @memberof IDargOpt
   */
  allowDragBorder: boolean;
}

/**
 * @description 面板可拖拽容器
 * @export
 * @class PanelDraggableContainerController
 * @extends {PanelContainerController<IPanelItem>}
 */
export class PanelDraggableContainerController extends PanelContainerController<IPanelItem> {
  declare state: PanelDraggableContainerState;

  /**
   * @description Interact 实例
   * @type {Interactable}
   * @memberof PanelDraggableContainerController
   */
  interactable?: Interactable;

  /**
   * @description 新建状态
   * @protected
   * @returns {*}  {PanelDraggableContainerState}
   * @memberof PanelDraggableContainerController
   */
  protected createState(): PanelDraggableContainerState {
    return new PanelDraggableContainerState(this.parent?.state);
  }

  /**
   * @description 初始化
   * @returns {*}  {Promise<void>}
   * @memberof PanelDraggableContainerController
   */
  async onInit(): Promise<void> {
    await super.onInit();
    this.state.minWidth = this.model.width || 300;
  }

  /**
   * @description 拖拽开始
   * @param {IData} event
   * @memberof PanelDraggableContainerController
   */
  onDragStart(event: IData): void {
    const { target } = event;

    // 禁止选择文本，避免拖动时出现选择效果
    document.body.style.userSelect = 'none';
    // 如果是第一次拖拽则需要设置窗口模式样式
    if (this.state.showMode !== 'window') {
      Object.assign(target.style, {
        width: `${this.state.minWidth}px`,
        height: `${this.state.minHeight}px`,
      });
      this.state.showMode = 'window';
    }
  }

  /**
   * @description 拖拽结束
   * @param {IData} event
   * @memberof PanelDraggableContainerController
   */
  onDragEnd(event: IData): void {
    // 恢复选择文本功能
    document.body.style.userSelect = '';
  }

  /**
   * @description 初始化拖拽
   * @private
   * @memberof PanelDraggableContainerController
   */
  initDraggable(): void {
    this.interactable = interact(`.ibiz-panel-draggable-container`)
      .draggable({
        enabled: true,
        allowFrom: '.drag-handle',
        modifiers: [
          // 保持在父对象内部
          interact.modifiers.restrictEdges({ outer: document.body }),
          // 缩放最小宽度
          interact.modifiers.restrictSize({
            min: { width: this.state.minWidth, height: this.state.minHeight },
          }),
        ],
        listeners: {
          start: event => this.onDragStart(event),
          end: event => this.onDragEnd(event),
          move: event => {
            const { target, rect } = event;
            Object.assign(target.style, {
              left: `${rect.left}px`,
              top: `${rect.top}px`,
            });
          },
        },
      })
      .resizable({
        enabled: false,
        // 可拖拽的边缘
        edges: {
          top: true,
          right: true,
          bottom: true,
          left: true,
        },
        margin: 6,
        modifiers: [
          // 保持在父对象内部
          interact.modifiers.restrictEdges({ outer: document.body }),
          // 缩放最小宽度
          interact.modifiers.restrictSize({
            min: { width: this.state.minWidth, height: this.state.minHeight },
          }),
        ],
        listeners: {
          start: event => this.onDragStart(event),
          end: event => this.onDragEnd(event),
          move: event => {
            const { target, rect } = event;
            Object.assign(target.style, {
              left: `${rect.left}px`,
              top: `${rect.top}px`,
              height: `${rect.height}px`,
              width: `${rect.width}px`,
            });
          },
        },
      });
  }

  /**
   * @description 更新拖拽配置
   * @param {IDargOpt} opt
   * @memberof PanelDraggableContainerController
   */
  updateDragOpt(opt: IDargOpt): void {
    this.interactable
      ?.draggable({ enabled: opt.allowDrag })
      .resizable({ enabled: opt.allowDragBorder });
  }
}
